复现环境
nccloud 2105
补丁
https://security.yonyou.com/#/noticeInfo?id=280
漏洞分析
漏洞接口为/uapjs/jsinvoke
,全局搜索jsinvoke
找到hotwebs/uapjs/WEB-INF/web.xml
文件中,/jsinvoke/*
路由对应的servlet
为nc.bs.framework.js.servlet.JsInvokeServlet
。
nc.bs.framework.js.servlet.JsInvokeServlet
这个类所在的jar
包并不在对应路径的lib
下,可以随便找个路由远程调试,使用idea的执行表达式功能,反射加载该类并输出该类在那个路径下,再将其添加到库中。
1 | Class clazz = Class.forName("nc.bs.framework.js.servlet.JsInvokeServlet"); |
跟入到JsInvokeServlet
中,调用了this.internal
的service
方法,this.internal
为nc.bs.framework.js.servlet.InternalJsInvokeServlet
类的实例对象,调用的也就是InternalJsInvokeServlet
的service
方法。
跟入InternalJsInvokeServlet
类中,service
调用了invoke
方法,在invoke
方法中首先调用了CommandFactory.getCommand(request)
去拿到一个ICommand
对象。
在CommandFactory.getCommand
方法中,从request
中获取action
参数,来创建不同的对象,并返回,在创建对象的时候会从request
中读取数据作为参数进行创建。漏洞利用的点是在InvokeCommand
对象中,所以需要传入invoke
。
回到nc.bs.framework.js.servlet.InternalJsInvokeServlet
的invoke
方法中,接下来会调用InvokeCommand
的execute
方法。
在execute
方法中,会将this.data
反序列化成MethodInvocation
对象,这里的this.data
是在CommandFactory.getCommand
方法中执行readJSONString(request);
被设置的,是请求时body
中传递的json
字符串,会将json的值赋值到MethodInvocation
对象的属性中。
主要的反射调用逻辑在handler.handleMethodInvocation(invocation)
,创建了MethodInvocationHandler
对象,以MethodInvocation
对象作为参数调用了handleMethodInvocation
方法。
跟入到MethodInvocationHandler
的handleMethodInvocation
方法中,通过ServiceName
作为参数调用NCLocator.getInstance().lookup(invocation.getServiceName())
方法,获得一个Object对象,在将这个对象作为参数调用invocation.invoke(stub)
。
首先是调用了NCLocator.getInstance()
拿到一个NCLocator
对象,拿到的是ServerNCLocator
对象。
随后会调用ServerNCLocator
的lookup
方法,紧接着会执行BusinessAppServer.getInstance().getServerContext().lookup(name);
,最终会调用到nc.bs.framework.server.AbstractContext
的lookup
方法。
在nc.bs.framework.server.AbstractContext
的lookup
方法中,过程看起来比较复杂。
首先会判断name
的前缀是否以->
开头,是的话会截取->
后的字符串,调用this.findMeta(name)
获取ComponentMeta
对象,然后以ComponentMeta
对象作为参数调用this.findComponent(meta)
获取Object
对象进行返回。
不是的话,会调用this.getServiceCache().get(name)
从缓存中拿到Object
对象,没在缓存中,会判断前缀是否以java:comp/env/
开头,那么会调用jndiCtx.lookup(name)
进行加载,拿到retObject
对象。
name
既不是以->
开头,也不是java:comp/env/
开头,会调用this.findMeta(name)
去获取ComponentMeta
对象。
跟入到this.findMeta(name)
方法中
最终会调用到nc.bs.framework.server.AbstractContainer
的getMeta
方法,this.publicRepo
是一个PublicMetaRepo
对象,metas
和nameIndices
为该类的属性。
getComponentMeta
方法则是通过name
在nameIndices
中查找对应的ComponentMeta
对象并返回。
最后会根据ComponentMeta
对象调用this.findComponent(meta)
。
获取实例并返回。
最后回到nc.bs.framework.js.rmi.MethodInvocationHandler
的handleMethodInvocation
方法中
跟入invocation.invoke(stub)
,对指定的serviceName
和methodName
进行反射调用。
通过如上分析,可以知道最终是可以反射调用一些特定的类的方法,并不是所有的类的方法都能够调用,只能是this.publicRepo
中nameIndices
这个属性中的,数量还比较多,大概有1w多个,那么只需要在这里面筛选出一些有危害的可利用的类进行反射调用来利用。
利用类
nc.bs.iufo.base.BaseSPService
就是保存在nameIndices
这个属性当中,该类的saveXStreamConfig
方法可传入url
和config
两个参数,url可控制写入路径,config可控制写入的文件内容,但是写入的文件内容是在xml中,这里选择用EL
表达式来进行利用。
1 | public void saveXStreamConfig(Object config, String url) { |
漏洞利用
首先需要写入动态执行js
表达式payload
1 | POST /uapjs/jsinvoke?action=invoke HTTP/1.1 |
命令执行回显
1 | POST /403.jsp HTTP/1.1 |
写入webshell
1 | POST /403.jsp HTTP/1.1 |
冰蝎连接 /501.jsp mzr@123
打入内存马
1 | POST /uapjs/jsinvoke?action=invoke HTTP/1.1 |
先访问 http://172.20.10.190:8080/403.jsp
,再连接
1 | 密码: mzr@123 |